package com.googlecode.contests_repo.validation;

import java.io.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.*;
import org.codehaus.jackson.map.ObjectMapper;

/**
 * A validation helper class used to validate the structure of contests.
 * 
 * @author joro, petko
 * 
 */
public class ContestValidator extends ResourceValidator {

	private static final String CONTEST_JSON_SCHEMA_FILE = "contest-schema.json";

	/**
	 * Checks whether the given contest folder contains valid data for the
	 * contest. Uses contest-schema.json for the validation.
	 * 
	 * @param folderPath
	 *            the path to the folder resource to validate
	 * @return a list of all validation errors
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public synchronized List<String> validate(String folderPath) {
		List<String> errors = super.validate(folderPath);
		if (errors.size() == 1 && errors.get(0).contains("does not exist")) {
			return errors;
		}
		File folder = new File(folderPath);

		// order / score attributes validation
		ObjectMapper mapper = new ObjectMapper();
		Collection scores = null;
		Collection orders = null;
		String dateString = "";
		File contestFile = new File(folder, "contest.json");
		try {
			Map<String, Object> map = mapper.readValue(contestFile, Map.class);
			scores = (Collection) map.get("problemScores");
			orders = (Collection) map.get("problemOrder");
			dateString = (String) map.get("startTime");
		} catch (Exception e) {
		}
		List<File> problemList = ValidationTools.getResourceSubfolders(folder);
		compareProblemOrderWithScores(errors, folder, problemList, orders,
				scores);

		// append contest problem errors
		ProblemValidator problemValidator = new ProblemValidator();
		for (File problem : problemList) {
			List<String> problemErrors = problemValidator.validate(problem
					.getAbsolutePath());
			errors.addAll(problemErrors);
		}

		// start time validation
		if (contestFile.exists() && !isDateStringValid(dateString)) {
			errors.add(String.format(
					"Folder %s: contest.json: malformatted start date time",
					folder.getAbsolutePath()));
		}

		totalFilesCount += problemList.size();
		int actualFilesCount = folder.list().length;
		if (actualFilesCount != totalFilesCount) {
			errors.add(String.format("Folder %s contains superfluous files.",
					folder));
		}

		return errors;
	}

	protected boolean jsonExists(List<String> errors, File folder,
			List<File> fileList) {
		File contestJasonFile = new File(folder, "contest.json");
		if (!fileList.contains(contestJasonFile)) {
			errors.add(String
					.format("Folder %s does not contain a \"contest.json\" file definition.",
							folder.getAbsolutePath()));
			return false;
		} else {
			List<String> validationErrors = ValidationTools.validateJson(
					contestJasonFile, Contest.class, CONTEST_JSON_SCHEMA_FILE);
			errors.addAll(validationErrors);
			return true;
		}
	}

	private boolean isDateStringValid(String dateString) {
		if (dateString == null) {
			return false;
		}

		DateFormat dateFormat = DateFormat.getDateInstance();
		try {
			dateFormat.parse(dateString);
		} catch (ParseException e) {
			return false;
		}

		return true;
	}

	private void compareProblemOrderWithScores(List<String> errors,
			File folder, List<File> problemList, Collection<String> orders,
			Collection<String> scores) {
		if (orders != null) {
			boolean foldersExist = ValidationTools.areFileNamesContained(
					orders, problemList);
			if (!foldersExist) {
				errors.add(String
						.format("Folder %s: The contest does not contain all problems mentioned in problem order.",
								folder.getAbsolutePath()));
			}

			if (scores != null) {
				if (orders.size() != scores.size()) {
					errors.add(String
							.format("Folder %s: contest.json: problemScores and problemOrder have different sizes.",
									folder.getAbsolutePath()));
				}
			}
		}
		if (scores != null && orders == null) {
			int problemCount = problemList.size();
			if (problemCount != scores.size()) {
				errors.add(String
						.format("Folder %s: Number of scores does not match the number of contest problems.",
								folder.getAbsolutePath()));
			}
		}
	}
}
